namespace FIREFLYX {
    /** 前向渲染 */
    export class ForwardRandering implements IExample {
        public Init(canvas: HTMLCanvasElement) {
            const gl = canvas.getContext("webgl2");
            //app init
            App.init(gl.canvas as any);
            ViewAspectManager.aspectRotateMode = AspectRotateMode.LANDSCAPE;
            SceneManager.renderContext.shadowDistance = 150;
            SceneManager.renderContext.shadowCascades = ShadowCascade.FOUR;
            //show P stats
            ShowPerformanceMonitor();

            //简单观察相机
            const cVEnv = GenCamViewEnv();
            const cam = cVEnv.camera;
            cam.near = 0.1;
            cam.far = 300;
            // cam.projection = ProjectionType.ORTHOGRAPHIC;
            // cam.size = 30;
            const camHover = cVEnv.camHover;
            camHover.distance = 10;

            //光斑大小（大于0）
            const spotSpec = 100;
            //texture
            let gridTex = DefaultAsset.GetTexture(DefTexture.GRID);
            let whiteTex = DefaultAsset.GetTexture(DefTexture.WHITE);
            //create mesh
            let sphereMesh = DefaultAsset.GetMesh(DefMesh.SPHERE);
            let quadMesh = DefaultAsset.GetMesh(DefMesh.QUAD);
            //引擎内建 shader
            const bSh = DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR);
            // const depthSh = DefaultAsset.GetShader(FIREFLYX.BUILDIN_SHADER_BASE_DEPTH);
            const shadowMapSh = DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_BASE_SHADOW_MAP);
            //材质
            //材质设置
            const makeBallMtr = (color: Color4, tex: Texture2D = gridTex) => {
                const mtr = new Material();
                mtr.shader = bSh;
                mtr.setProperty(`u_mainColor`, color);
                mtr.setProperty(`ubo_material_block.u_spotSpec`, spotSpec);
                mtr.setProperty(`u_mainTex`, tex);
                return mtr;
            };
            const makeShadowMapMtr = () => {
                const mtr = new Material();
                mtr.shader = shadowMapSh;
                mtr.lightMode = RenderPipeline.LM_SHADOW_CASTER;
                return mtr;
            };
            const mtr01 = makeBallMtr(new Color4(1, 0, 0, 1));
            const mtr02 = makeBallMtr(new Color4(0, 1, 0, 0.2));
            mtr02.blendState = BlendState.getState(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA, BlendEquation.FUNC_ADD);
            mtr02.depthState = DepthState.getState(false);
            mtr02.renderQueue = RenderContext.RENDER_QUEUE_TRANSPARENT + 500;
            const mtr03 = makeBallMtr(new Color4(0, 0, 1, 0.7));
            mtr03.blendState = BlendState.getState(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA, BlendEquation.FUNC_ADD);
            mtr03.depthState = DepthState.getState(false);
            mtr03.renderQueue = RenderContext.RENDER_QUEUE_TRANSPARENT + 500;

            const mg01 = [mtr01, makeShadowMapMtr()];
            const mg02 = [mtr02, makeShadowMapMtr()];
            const mg03 = [mtr03, makeShadowMapMtr()];

            const mgs = [mg01, mg02, mg03];

            //创建模型
            let modelCount = 0;
            const makeModel = (mats: MaterialGroup, _mesh: Mesh) => {
                const model = new GameObject(`model_${modelCount}`);
                modelCount++;
                //模型渲染组件
                const mr = model.addComponent<MeshRenderer>(MeshRenderer);
                mr.materialGroupList[0] = mats;
                mr.mesh = _mesh;
                return model;
            };

            //主光源 
            //方向光源
            const lightNode = new GameObject("lightNode");
            lightNode.transform.localPostion = new Vector3(5, 10, 0);
            const light = lightNode.addComponent<Light>(Light);
            light.color = new Color4(1, 1, 0.8, 1);                 //光源色
            light.type = LightType.DIRECTIONAL;
            light.shadowNear = -10;  //调整近面
            light.shadowMapResolution = ShadowMapResolution.MEDIUM;
            // light.shadowMapResolution = ShadowMapResolution.HIGH;

            //球整列 模型节点
            const modelSize = new Vector3(2, 2, 2);
            const gap = 3;  //间隔距离
            const scaleMat = Matrix.Scaling(gap, gap, gap);
            const offsetPos = new Vector3(modelSize.x - 1, modelSize.y - 1, modelSize.z - 1);
            offsetPos.scaleToRef(-0.5 * gap, offsetPos);
            let count = 0;
            for (let z = 0; z < modelSize.z; z++) {
                for (let y = 0; y < modelSize.y; y++) {
                    for (let x = 0; x < modelSize.x; x++) {
                        //
                        const mg = mgs[count % mgs.length];
                        const node = makeModel(mg, sphereMesh);
                        node.transform.scale = new Vector3(1.5, 1, 2);
                        const trans = node.transform;
                        const pos = trans.localPostion;
                        pos.set(x, y, z);
                        Vector3.TransformCoordinatesToRef(pos, scaleMat, pos);
                        pos.addToRef(offsetPos, pos);

                        trans.localPostion = pos;

                        count++;
                    }
                }
            }

            //一个平面
            const floorMg = [makeBallMtr(new Color4(1, 1, 1, 1), whiteTex), makeShadowMapMtr()];
            const floor = makeModel(floorMg, quadMesh);
            floor.transform.localPostion = new Vector3(0, -2, 0);
            floor.transform.localScale = new Vector3(30, 30, 1);
            Quaternion.RotationYawPitchRollToRef(0, 90 * toRadian, 0, floor.transform.localRotation);
            floor.transform.localRotation = floor.transform.localRotation;

            const makeFrustum = () => {
                const result: Vector3[] = [];
                for (let i = 0; i < 8; i++) {
                    result.push(new Vector3());
                }
                return result;
            };
            const lFData: LightFrustumData = new LightFrustumData(0.1, 50, 20, 20);
            lFData.position.copyFrom(lightNode.transform.position);
            lFData.rotation.copyFrom(lightNode.transform.rotation);
            //相机视锥 
            const camFrustum: Vector3[] = makeFrustum();
            //灯光Frustum 世界点数据
            const lightFrustum: Vector3[] = makeFrustum();

            /** 光源锥数据 填充到 meshData */
            const fullLightFrustumMeshData = (lightFrustum: Vector3[], lightFrustumMeshData: MeshData) => {
                //
                const posArr = new Float32Array(24 * 3);
                let arrIndex = 0;
                const linkLine = (aIdx: number, bIdx: number) => {
                    const aPos = lightFrustum[aIdx];
                    const bPos = lightFrustum[bIdx];
                    const currArrIdx = arrIndex;
                    posArr[currArrIdx] = aPos.x;
                    posArr[currArrIdx + 1] = aPos.y;
                    posArr[currArrIdx + 2] = aPos.z;
                    posArr[currArrIdx + 3] = bPos.x;
                    posArr[currArrIdx + 4] = bPos.y;
                    posArr[currArrIdx + 5] = bPos.z;
                    arrIndex += 6;
                };

                //nearFrame
                linkLine(0, 1);
                linkLine(1, 2);
                linkLine(2, 3);
                linkLine(3, 0);
                //farFrame
                linkLine(4, 5);
                linkLine(5, 6);
                linkLine(6, 7);
                linkLine(7, 4);
                //center edges
                linkLine(0, 4);
                linkLine(1, 5);
                linkLine(2, 6);
                linkLine(3, 7);

                lightFrustumMeshData.posArr = posArr;
            };

            /**
             * 更新灯光Frustum数据
             * @param camFrustum 
             * @param lightFrustum 
             * @param lightFrustumMeshData 
             * @param lightFrustumMesh 
             */
            const refreshLightFrustum = (camFrustum: Vector3[], lightFrustum: Vector3[], lightFrustumMeshData: MeshData, lightFrustumMesh: Mesh) => {
                //
                //光锥更新
                const lightCFZLimit = 30;
                cam.calcFrustumPoints(cam.near, cam.far > lightCFZLimit ? lightCFZLimit : cam.far, camFrustum);
                // fullLightFrustumMeshData(camFrustum, lightFrustumMeshData);

                Light.calcDirLightFrustumData(camFrustum, lightNode.transform.rotation, lFData);
                Light.calcDirLightFrustumPoints(lFData, lightFrustum);
                fullLightFrustumMeshData(lightFrustum, lightFrustumMeshData);
                // light.setVPMatrix(lFData.w, lFData.h, lFData.far, lFData.position);

                lightFrustumMesh.apply();
            };

            //灯光Frustum 网格
            const lightFrustumMeshData = new MeshData();
            const lightFrustumMesh = new Mesh("lightFrustumMesh");
            lightFrustumMesh.data = lightFrustumMeshData;
            refreshLightFrustum(camFrustum, lightFrustum, lightFrustumMeshData, lightFrustumMesh);
            lightFrustumMesh.primitives[0].mode = gl.LINES; //mesh.apply()之后会生成一个默认数据，改为线绘制模式
            //绘制线材质
            const lineMtr = new Material("lineMaterial");
            lineMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_SIMPLE_LINE);
            //方向光源的锥体节点
            const lightFrustumNode = new GameObject("lightFrustum");
            const lFMR = lightFrustumNode.addComponent<MeshRenderer>(MeshRenderer);
            lFMR.materialGroup[0] = lineMtr;
            lFMR.mesh = lightFrustumMesh;

            let angles = new Vector3(45 * toRadian, 90 * toRadian, 0);
            // let angles = new Vector3(90 * toRadian, 0, 0);
            // let angles = new Vector3();
            let speed = 0.1;

            GlobalEventManager.Instance.addListener("sceneUpdate", (dt: number) => {
                //旋转
                Quaternion.RotationYawPitchRollToRef(angles.y, angles.x, angles.z, lightNode.transform.rotation);
                lightNode.transform.rotation = lightNode.transform.rotation;
                // lightNode.transform.position = cam.entity.transform.position;
                // lightNode.transform.rotation = cam.entity.transform.rotation;

                //光锥更新
                refreshLightFrustum(camFrustum, lightFrustum, lightFrustumMeshData, lightFrustumMesh);
            }, this);

            //IO Event
            //键盘控制旋转
            GlobalEventManager.Instance.addListener("keydown", (ev: KeyboardEvent) => {
                console.log(`onkeydown : ${ev.key} `);
                let dt = 1 * speed;
                switch (ev.key) {
                    case "a": angles.x += dt; break;
                    case "w": angles.y += dt; break;
                    case "s": angles.z += dt; break;
                    default:
                }
            }, this);
        }
    }
}